// write alert log and send alert messages

package util

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/go-kit/log/level"
	"github.com/rabbitmq/amqp091-go"
)

const p = "pause"

var lock sync.Mutex

//var mqConn amqp091.Connection

//WriteAndAlert 当前时间报警信息写入标记文件和执行报警
//func WriteAndAlert(fname string, alstr string, ip string, projectname string, team string, log *log.Logger) {
func WriteAndAlert(fname string, alstr string) {
	level.Warn(Logger).Log("len alstr", len(alstr))
	dataByte, err := ioutil.ReadFile(fname)
	if err != nil {
		level.Error(Logger).Log("err", err)
		return
	}
	if strings.Contains(string(dataByte), p) {
		level.Warn(Logger).Log("notice", "维护时间段，不报警")
		//log.Println("维护时间段，不报警")
		return
	}
	// 当没有报警的时候每次都把报警文件清空，以防干扰
	if len(alstr) == 0 {
		//write(fname, alstr, "")
		os.Truncate(fname, 0)
		return
	}
	// 当有报警时且报警记录文件内容为空时，写入最新的报警到文件中并报警，否则要现比较报警文件和报警信息
	if string(dataByte) == "" {
		write(fname, alstr, Sconf.Server.Ipaddr)
		//alert(ip, alstr, projectname)
		// sendaAlert需要更新
		// sendAlert(alerts) alerts = Alert{}
		sendAlert(alstr)
	} else {
		data := strings.TrimSuffix(string(dataByte), "\n")
		if ok := compare(Sconf.Server.Ipaddr+" "+alstr, data); !ok {
			write(fname, alstr, Sconf.Server.Ipaddr)
			//alert(ip, alstr, projectname)
			sendAlert(alstr)
		} else {
			level.Warn(Logger).Log("alert mesg", alstr, "notice", "宕机资源重复，不进行报警")
			//log.Println(alstr, "宕机资源重复，不进行报警")
		}
	}
}

//}

//清空并写入记录
func write(fname string, alstr string, ip string) {
	lock.Lock()
	// p, _ := filepath.Abs(filepath.Dir(os.Args[0]))
	// path := p + "/" + fname
	os.Truncate(fname, 0)
	f, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE, os.ModePerm)
	if err != nil {
		level.Error(Logger).Log("err", err)
		return
	}
	defer lock.Unlock()
	defer f.Close()
	f.WriteString(ip + " " + alstr)
}

// 发送报警信息到api
func sendAlert(val string) {
	anno := Annotations{
		Description: val,
		Call:        Sconf.Server.Call,
		Summary:     "进程异常报警",
	}
	labels := Labels{
		Alertname: "进程报警",
		Altype:    Sconf.Server.Altype,
		App:       Sconf.Server.Project,
		Cluster:   Sconf.Server.Cluster,
		Name:      Sconf.Server.Name,
		Instance:  FetchIP(Sconf.Server.Nic),
		Team:      Sconf.Server.Team,
	}
	msg := Alerts{
		Status:      "firing",
		Labels:      labels,
		Annotations: anno,
		StartsAt:    time.Now(),
	}
	conn, err := amqp091.Dial(fmt.Sprintf("amqp://%s:%s@%s/", Sconf.Rabbitmq.User, Sconf.Rabbitmq.Pass, Sconf.Rabbitmq.Url))
	if err != nil {
		level.Error(Logger).Log("failed to connect rabbitmq:", err)
		return
	}
	defer conn.Close()
	ch, err := conn.Channel()
	if err != nil {
		level.Error(Logger).Log("failed to open a rabbit channel")
		return
	}
	defer ch.Close()
	alertInfo, _ := json.Marshal(&msg)
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	err = ch.PublishWithContext(
		ctx,
		"",
		Sconf.Rabbitmq.Queue,
		false,
		false,
		amqp091.Publishing{
			ContentType: "application/json",
			Body:        alertInfo,
		},
	)
	if err != nil {
		level.Error(Logger).Log("failed to to publish alert to rabbitmq")
		return
	}
	level.Debug(Logger).Log("[X] send alert msg", string(alertInfo))
	// // log.Println(string(d))
	// req, err := http.NewRequest("POST", Sconf.Server.Alerturl, bytes.NewBuffer(d))
	// if err != nil {
	// 	level.Error(Logger).Log("err", err)
	// 	return
	// }
	// // 不复用客户端
	// req.Close = true
	// req.Header.Set("Content-type", "application/json")
	// client := &http.Client{}
	// resp, err := client.Do(req)
	// if err != nil {
	// 	level.Error(Logger).Log("err", err)
	// 	return
	// }
	// defer resp.Body.Close()
	// level.Info(Logger).Log("send status", resp.Status)
	// // log.Println("status: ", resp.Status)
}

// 比较文件内容和内存字符串, true代表"相等",不许报警，false需要报警
func compare(strNew, strOld string) bool {
	var newSlice []string
	//真实进程信息
	sliceNew := strings.Split(strNew, " ")
	//配置文件进程信息
	sliceOld := strings.Split(strOld, " ")

	for _, value := range sliceNew {
		if strings.Contains(strOld, value) {
			newSlice = append(newSlice, value)
		} else {
			return false
		}
	}
	if len(newSlice) == len(sliceOld) {
		return true
	} else {
		return false
	}
}

// Alerts报警结构体
type Alerts struct {
	Status      string      `json:"status"`
	Labels      Labels      `json:"labels"`
	Annotations Annotations `json:"annotations"`
	StartsAt    time.Time   `json:"startsAt"`
	EndsAt      time.Time   `json:"endsAt"`
	//GeneratorURL string      `json:"generatorURL"`
	//Fingerprint  string      `json:"fingerprint"`
}
type Annotations struct {
	Description string `json:"description"`
	Summary     string `json:"summary"`
	Call        string `json:"call"` // 是否需要电话通知
}
type Labels struct {
	Alertname string `json:"alertname"`
	Altype    string `json:"altype"`
	App       string `json:"app"`
	Cluster   string `json:"cluster"`
	Instance  string `json:"instance"`
	Job       string `json:"job"`
	Name      string `json:"name"`
	Region    string `json:"region"`
	Team      string `json:"team"`
}
